pg = require "pg"

connectAsync = ->
  args = Array.from arguments
  new Promise (resolve,reject)->
    callback = (err,client,done)->
      if err
        reject err
        return
      resolve {client:client,done:done}
      return
    args.push callback
    pg.connect.apply pg,args
    return
queryAsync = (client)->
  args = Array.from arguments
  args.shift()
  new Promise (resolve,reject)->
    callback = (err,result)->
      if err
        reject err
        return
      resolve result
      return
    args.push callback
    client.query.apply client,args
    return
exports.PostgresqlDao = new Class(
  Implements: [Events, Options]
  options:
    dbName: undefined
    jdbcDb: undefined
    #连接字符串
    #conString: undefined
  initialize: (options)->
    t = this
    t.setOptions options
    ###
    o = t.options
    jdbcDb = o.jdbcDb
    jdbcDb.database = "" if !jdbcDb.database
    jdbcDb.user = "postgres" if !jdbcDb.user
    jdbcDb.host = "localhost" if !jdbcDb.host
    o.conString = "postgres://"+jdbcDb.user+":"+jdbcDb.password+"@"+jdbcDb.host+"/"+jdbcDb.database
    ###
    t
  #创建临时表
  crtTmpTab: (reqOpt)->
    t = this
    o = t.options
    _uid = reqOpt._uid
    if !_uid
      _uid = String.uniqueID()
      reqOpt._uid = _uid
    tableName = "temp_table_"+_uid
    sql = "create temp table "+t.escapeId(tableName)+" (\"id\" serial PRIMARY KEY) ON COMMIT PRESERVE ROWS;"
    yield t.callSql reqOpt,sql
    reqOpt.tmpTableArr.push tableName
    tableName
  #删除临时表
  drpTmpTab: (reqOpt,tabName)->
    t = this
    o = t.options
    if !tabName
      if reqOpt.tmpTableArr
        for tabName0 in reqOpt.tmpTableArr
          sql = "drop table if EXISTS "+t.escapeId tabName0
          yield t.callSql reqOpt,sql
        reqOpt.tmpTableArr = []
    else
      sql = "drop table if EXISTS "+t.escapeId tabName
      yield t.callSql reqOpt,sql
      reqOpt.tmpTableArr.erase tabName if reqOpt.tmpTableArr
    return
  escapeId: (str)-> pg.Client.prototype.escapeIdentifier str
  getDb_type: (reqOpt)->
    t = this
    o = t.options
    jdbcDb = o.jdbcDb
    db_type = jdbcDb[o.dbName].db_type
    db_type
  callSp: (reqOpt,sp,spArg,rsNum,fetch)->
    t = this
    o = t.options
    conn2 = reqOpt.conn2
    jdbcDb = o.jdbcDb
    #是否内部事务,如果是内部事务,则在这个方法内部提交或者回滚
    isInnerTran = !conn2
    #{client:client,done:done}
    if isInnerTran
      conn2 = yield connectAsync jdbcDb[o.dbName]
      console.log "begin;"
      yield queryAsync conn2.client,"begin",null
    spIde = ""
    spArr = sp.split "\."
    if spArr.length > 1
      spIde = conn2.client.escapeIdentifier(spArr[0])+"."+conn2.client.escapeIdentifier(spArr[1])
    sql = "select * from "+spIde+"("
    spArg2 = []
    isFirst = true
    if rsNum
      for i in [0...rsNum]
        if !isFirst
          sql += ","
        else
          isFirst = false
        tmpNum = spArg2.push "f"+(i+1)
        sql += "$"+tmpNum
    if spArg
      for arg,i in spArg
        if !isFirst
          sql += ","
        else
          isFirst = false
        tmpNum = spArg2.push arg
        sql += "$"+tmpNum
    sql += ")"
    #console.log sql
    #args_debug = JSON.stringify(spArg2).replace /"/gm,"'"
    #args_debug = args_debug.substring 1,args_debug.length-1
    #console.log args_debug
    rltSet = []
    console.time "sql"
    if isInnerTran
      try
        rltSetTmp = yield t.callSql reqOpt,sql,spArg2,rsNum
        if rsNum isnt 0
          if fetch isnt false
            for i in [0...rsNum]
              rltFth = yield queryAsync conn2.client,"FETCH ALL IN f"+(i+1),null
              rltSet.push rltFth.rows
        else
          rltSet = rltSetTmp
      catch err
        console.log "rollback;"
        try
          yield queryAsync conn2.client,"rollback",null
        catch err
          console.error err
        conn2.done()
        throw err
      console.log "commit;"
      try
        yield queryAsync conn2.client,"commit",null
      catch err
        console.error err
      conn2.done()
    else
      rltSetTmp = yield t.callSql reqOpt,sql,spArg2,rsNum
      if rsNum isnt 0
        if fetch isnt false
          for i in [0...rsNum]
            rltFth = yield queryAsync conn2.client,"FETCH ALL IN f"+(i+1),null
            rltSet.push rltFth.rows
      else
        rltSet = rltSetTmp
    yield queryAsync conn2.client,"commit",null
    console.timeEnd "sql"
    rltSet
  callSql: (reqOpt,sql,spArg,rsNum)->
    rltSet = null
    t = this
    o = t.options
    conn2 = reqOpt.conn2
    jdbcDb = o.jdbcDb
    try
      sql_debug = sql.replace /\$\d+/gm,(strTmp)->
        numTmp = Number strTmp.substring 1
        numTmp--
        return 'NULL' if spArg[numTmp] is undefined or spArg[numTmp] is null
        "'"+spArg[numTmp]+"'"
      console.log sql_debug+";"
    catch err
      console.error err
    isInnerTran = !conn2
    conn2 = yield connectAsync jdbcDb[o.dbName] if isInnerTran
    try
      rltSet = yield queryAsync conn2.client,sql,spArg
    catch err
      conn2.done() if isInnerTran
      throw err
    conn2.done() if isInnerTran
    rltSet
  getConn: (reqOpt)->
    t = this
    o = t.options
    jdbcDb = o.jdbcDb
    conn2 = yield connectAsync jdbcDb[o.dbName]
    reqOpt.conn2 = conn2
    conn2
  endConn: (reqOpt)->
    t = this
    o = t.options
    conn2 = reqOpt.conn2
    return if !conn2
    conn2.done();
    return
  beginTran: (reqOpt)->
    console.log "begin;"
    conn2 = reqOpt.conn2
    yield queryAsync conn2.client,"begin",null
  commitTran: (reqOpt)->
    conn2 = reqOpt.conn2
    return if !conn2
    console.log "commit;"
    rv = yield queryAsync conn2.client,"commit",null
    conn2.done()
    rv
  rollbackTran: (reqOpt)->
    conn2 = reqOpt.conn2
    return if !conn2
    console.log "rollback;"
    rv = yield queryAsync conn2.client,"rollback",null
    conn2.done()
    rv
  setAutoCommit: (reqOpt,atc)->
    conn2 = reqOpt.conn2
    autocommit = "on"
    if atc is false
      autocommit = "off"
    yield queryAsync conn2.client,"set autocommit "+autocommit,null
)